package net.butfly.bus.server;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import net.butfly.albacore.exception.SystemException;
import net.butfly.bus.Constants;
import net.butfly.bus.config.bean.InvokerBean;
import net.butfly.bus.config.bean.NodeBean;
import net.butfly.bus.config.detail.InvokerConfig;
import net.butfly.bus.config.parser.XMLConfigParser;
import net.butfly.bus.impl.InvokerProvider;
import net.butfly.bus.server.config.ServerConfig;
import net.butfly.bus.server.config.bean.RouterBean;
import net.butfly.bus.server.config.bean.TxBean;
import net.butfly.bus.server.invoker.InvokerTypeFactory;
import net.butfly.bus.util.TXUtils;

import org.dom4j.Attribute;
import org.dom4j.Element;

public class XMLConfigServerParser extends XMLConfigParser {
	// private Element element;

	public XMLConfigServerParser(InputStream source) {
		super(source);
	}

	@SuppressWarnings("unchecked")
	@Override
	public ServerConfig parse() {
		ServerConfig config = new ServerConfig();
		super.parse(config);
		config.setNodesMap(this.parseNodes("local/node"));
		config.setTxMap(this.parseTX());
		config.setRouter(this.parseRouter());
		config.setBusID(this.parseBusID());
		return config;
	}

	private String parseBusID() {
		String id = this.root.attributeValue("id");
		return id == null ? UUID.randomUUID().toString() : id;
	}

	@Override
	protected NodeBean createNodeBean(String nodeId, Element nodeEle) {
		InvokerBean invokerBean = this.parseInvoker(nodeId, nodeEle);
		return new NodeBean(nodeId, nodeEle.attributeValue("txMatching"), null, invokerBean, null);
	};

	private InvokerBean parseInvoker(String nodeId, Element nodeEle) {
		String id = nodeEle.attributeValue("invoker");// 调用类型
		Element element;
		if (id != null) {
			element = (Element) this.root.selectSingleNode("local/invoker[@id='" + id + "']");
			if (element == null) {
				throw new SystemException(Constants.UserError.NODE_INVOKER_NOT_FOUND, "没有找到 id为 \"" + nodeId
						+ "\" 的 node 所配置的 id为 \"" + id + "\" invoker");
			}
		} else {
			element = (Element) nodeEle.selectSingleNode("invoker");
			if (element != null) {
				throw new SystemException(Constants.UserError.NODE_INVOKER_NOT_FOUND, "id为\"" + nodeId
						+ "\"的 node 没有配置 connection");
			}
		}
		String type = element.attributeValue("type");
		if (type == null) {
			throw new SystemException(Constants.UserError.NODE_INVOKER_NOT_FOUND, "id为 \"" + id + "\" 的 invoker 没有配置 type 属性");
		}
		logger.info("节点(Id:" + nodeId + ")对应Invoker:\n	" + element.asXML());
		Class<? extends InvokerProvider> clazz = classForName(type);
		InvokerConfig config = InvokerTypeFactory.getConfig(clazz);

		if (config == null) {
			throw new SystemException(Constants.UserError.NODE_INVOKER_NOT_FOUND, "can not found invoker type with name \""
					+ type + "\" on nodes loading");
		}
		try {
			processConfigObj(config, element);
		} catch (Exception ex) {
			throw new SystemException(Constants.UserError.NODE_INVOKER_NOT_FOUND, "设置id为 " + id + " 的 invoker 参数出错", ex);
		}
		InvokerProvider iip = InvokerTypeFactory.getProvider(clazz, config);
		if (iip != null) {
			logger.info("节点(Id:" + nodeId + ")对应Invoker(类型:" + type + ")初始化...");
			iip.init();
		} else {
			throw new SystemException(Constants.UserError.NODE_CONFIG, "节点(Id:" + nodeId + ")配置的Invoker(类型:" + type
					+ ")配置错误，系统没有找到相应的InvokerProvider实例.");
		}
		return new InvokerBean(id, clazz, config);
	}

	@SuppressWarnings("unchecked")
	private static void processConfigObj(Object configObj, Element element) throws IllegalArgumentException,
			IllegalAccessException {
		Class<?> clazz = configObj.getClass();
		while (!clazz.equals(Object.class)) {
			Field[] fAry = clazz.getDeclaredFields();
			if (fAry != null) {
				for (Field f : fAry) {
					Object v = null;
					if (List.class.isAssignableFrom(f.getType())) {// list类型
						List<Element> eleList = element.selectNodes(f.getName());
						if (eleList != null) {
							List<Object> itemList = new ArrayList<Object>();
							Object to;
							for (Element ele : eleList) {
								to = processConfigTag(ele);
								if (to != null) {
									itemList.add(to);
								}
							}
							v = itemList;
						}
					} else {
						Element ele = (Element) element.selectSingleNode(f.getName());
						if (ele != null) {
							Object to = processConfigTag(ele);
							if (to != null) {
								v = to;
							}
						}
					}
					if (v != null) {
						boolean af = f.isAccessible();
						f.setAccessible(true);
						f.set(configObj, v);
						f.setAccessible(af);
					}
				}
			}
			clazz = clazz.getSuperclass();
		}
	}

	private static Object processConfigTag(Element element) {
		if (element.attributeCount() > 0) {
			Map<String, String> rtn = new HashMap<String, String>();
			Iterator<?> it = element.attributeIterator();
			Attribute at;
			while (it.hasNext()) {
				at = (Attribute) it.next();
				rtn.put(at.getName(), at.getValue());
			}
			return rtn;
		}
		String tt = element.getTextTrim();
		if (tt != null && !tt.equals("")) {
			return tt;
		}
		return null;
	}

	private HashMap<String, TxBean> parseTX() {
		List<Element> eleList = this.elements("local/tx");
		HashMap<String, TxBean> txMap = new HashMap<String, TxBean>();
		if (eleList != null) {
			String[] nodeIdAry;
			TxBean txBean;
			for (Element ele : eleList) {
				String ts = ele.attributeValue("nodes");
				if (ts != null) {
					nodeIdAry = ts.split(",");
				} else {
					nodeIdAry = null;
				}
				txBean = new TxBean(ele.attributeValue("code"), ele.attributeValue("version"), nodeIdAry);
				logger.info("加载交易(交易代码 " + txBean.getCode() + "(" + txBean.getVersion() + ")" + ",节点位置 " + nodeIdAry + ").");
				txMap.put(TXUtils.key(txBean.getCode(), txBean.getVersion()), txBean);
			}
		}
		return txMap;
	}

	private RouterBean parseRouter() {
		Element routerEle = (Element) this.elements("local/router");
		if (routerEle != null)
			return new RouterBean(routerEle.attributeValue("type"), routerEle.attributeValue("directory"),
					routerEle.attributeValue("policy"));
		else
			return new RouterBean("default", "simple", "random");// 默认路由器
	}
}
